<template>
  <div :class="classes" v-clickoutside="handleClose">
    <div
      :class="[prefixCls + '-selection']"
      ref="reference"
      @click="toggleMenu">
      <div class="oolong-tag" v-for="(item, index) in selectedMultiple">
        <span class="oolong-tag-text">{{ item.label }}</span>
        <oolongIcon type="ios-close-empty" @click.native.stop="removeTag(index)"></oolongIcon>
      </div>
      <span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ placeholder }}</span>
      <span :class="[prefixCls + '-selected-value']" v-show="(!multiple && !showPlaceholder && !filterable)">{{ selectedSingle }}</span>
      <input
        type="text"
        v-if="filterable"
        v-model="query"
        :class="[prefixCls + '-input']"
        :placeholder="showPlaceholder ? localePlaceholder : ''"
        :style="inputStyle"
        @blur="handleBlur"
        @keydown="resetInputState"
        @keydown.delete="handleInputDelete"
        ref="input">
      <oolongIcon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon"
                  @click.native.stop="clearSingleSelect"></oolongIcon>
      <oolongIcon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></oolongIcon>
    </div>
    <transition :name="transitionName">
      <oolongSelectDropdown v-show="dropVisible" :placement="placement" ref="dropdown">
        <ul v-show="notFountShow" :class="[prefixCls + '-not-found']">
          <li>{{ notFoundText }}</li>
        </ul>
        <ul v-show="(!notFound && !remote) || (remote && !loading && !notFound)"
            :class="[prefixCls + '-dropdown-list']">
          <slot></slot>
        </ul>
        <ul v-show="loading" :class="[prefixCls + '-loading']">{{ loadingText }}</ul>
      </oolongSelectDropdown>
    </transition>
  </div>
</template>
<script>

  import oolongIcon from '../icon';
  import oolongSelectDropdown from './oolong-select-dropdown.vue';
  import clickoutside from '../../directives/clickoutside';
  import {oneOf, findComponentDownward} from '../../utils/assist';
  import Emitter from '../../mixins/emitter';
  import Locale from '../../mixins/locale';

  const prefixCls = 'oolong-select';

  export default {
    name: 'oolongSelect',
    mixins: [Emitter, Locale],
    components: {oolongIcon, oolongSelectDropdown},
    directives: {clickoutside},
    props: {
      value: {
        type: [String, Number, Array],
        default: ''
      },
      label: {
        type: [String, Number, Array],
        default: ''
      },
      multiple: {
        type: Boolean,
        default: false
      },
      disabled: {
        type: Boolean,
        default: false
      },
      clearable: {
        type: Boolean,
        default: false
      },
      placeholder: {
        type: String
      },
      filterable: {
        type: Boolean,
        default: false
      },
      filterMethod: {
        type: Function
      },
      remote: {
        type: Boolean,
        default: false
      },
      remoteMethod: {
        type: Function
      },
      loading: {
        type: Boolean,
        default: false
      },
      loadingText: {
        type: String
      },
      size: {
        validator (value) {
          return oneOf(value, ['small', 'large', 'default']);
        }
      },
      labelInValue: {
        type: Boolean,
        default: false
      },
      notFoundText: {
        type: String
      },
      placement: {
        validator (value) {
          return oneOf(value, ['top', 'bottom']);
        },
        default: 'bottom'
      }
    },
    data () {
      return {
        prefixCls: prefixCls,
        visible: false,
        options: [],
        optionInstances: [],
        selectedSingle: '',    // label
        selectedMultiple: [],
        focusIndex: 0,
        query: '',
        lastQuery: '',
        selectToChangeQuery: false,    // when select an option, set this first and set query, because query is watching, it will emit event
        inputLength: 20,
        notFound: false,
        slotChangeDuration: false,    // if slot change duration and in multiple, set true and after slot change, set false
        model: this.value
      };
    },
    computed: {
      classes () {
        return [
          `${prefixCls}`,
          {
            [`${prefixCls}-visible`]: this.visible,
            [`${prefixCls}-disabled`]: this.disabled,
            [`${prefixCls}-multiple`]: this.multiple,
            [`${prefixCls}-single`]: !this.multiple,
            [`${prefixCls}-show-clear`]: this.showCloseIcon,
            [`${prefixCls}-${this.size}`]: !!this.size
          }
        ];
      },
      showPlaceholder () {
        let status = false;

        if ((typeof this.model) === 'string') {
          if (this.model === '') {
            status = true;
          }
        } else if (Array.isArray(this.model)) {
          if (!this.model.length) {
            status = true;
          }
        }

        return status;
      },
      showCloseIcon () {
        return !this.multiple && this.clearable && !this.showPlaceholder;
      },
      inputStyle () {
        let style = {};

        if (this.multiple) {
          if (this.showPlaceholder) {
            style.width = '100%';
          } else {
            style.width = `${this.inputLength}px`;
          }
        }

        return style;
      },
      localePlaceholder () {
        if (this.placeholder === undefined) {
          return this.t('i.select.placeholder');
        } else {
          return this.placeholder;
        }
      },
      localeNotFoundText () {
        if (this.notFoundText === undefined) {
          return this.t('i.select.noMatch');
        } else {
          return this.notFoundText;
        }
      },
      localeLoadingText () {
        if (this.loadingText === undefined) {
          return this.t('i.select.loading');
        } else {
          return this.loadingText;
        }
      },
      transitionName () {
        return this.placement === 'bottom' ? 'slide-up' : 'slide-down';
      },
      dropVisible () {
        let status = true;
        const options = this.$slots.default || [];
        if (!this.loading && this.remote && this.query === '' && !options.length) status = false;
        return this.visible && status;
      },
      notFountShow () {
        const options = this.$slots.default || [];
        return (this.notFound && !this.remote) || (this.remote && !this.loading && !options.length);
      }
    },
    methods: {
      toggleMenu () {
        if (this.disabled) {
          return false;
        }

        this.visible = !this.visible;
      },
      hideMenu () {
        this.visible = false;
        this.focusIndex = 0;
        this.broadcast('oolongOption', 'on-select-close');
      },
      // find option component
      findChild (cb,_this) {
          alert(cb);
        const find = function (child) {
          const name = child.$options.componentName;

          if (name) {
            cb(child,_this);
          } else if (child.$children.length) {
            child.$children.forEach((innerChild) => {
              find(innerChild, cb);
            });
          }
        };

        if (this.optionInstances.length) {
          this.optionInstances.forEach((child) => {
            find(child);
          });
        } else {
          this.$children.forEach((child) => {
            find(child);
          });
        }
      },
      updateOptions (init, slot = false) {
        let options = [];
        let index = 1;

        this.findChild((child) => {
          options.push({
            value: child.value,
            label: (child.label === undefined) ? child.$el.innerHTML : child.label
          });
          child.index = index++;

          if (init) {
            this.optionInstances.push(child);
          }
        });

        this.options = options;

        if (init) {
          if (!this.remote) {
            this.updateSingleSelected(true, slot);
            this.updateMultipleSelected(true, slot);
          }
        }
      },
      updateSingleSelected (init = false, slot = false) {
        const type = typeof this.model;

        if (type === 'string' || type === 'number') {
          let findModel = false;

          for (let i = 0; i < this.options.length; i++) {
            if (this.model === this.options[i].value) {
              this.selectedSingle = this.options[i].label;
              findModel = true;
              break;
            }
          }

          if (slot && !findModel) {
            this.model = '';
            this.query = '';
          }
        }

        this.toggleSingleSelected(this.model, init);
      },
      clearSingleSelect () {
        if (this.showCloseIcon) {
          this.findChild((child) => {
            child.selected = false;
          });
          this.model = '';

          if (this.filterable) {
            this.query = '';
          }
        }
      },
      updateMultipleSelected (init = false, slot = false) {
        if (this.multiple && Array.isArray(this.model)) {
          let selected = this.remote ? this.selectedMultiple : [];

          for (let i = 0; i < this.model.length; i++) {
            const model = this.model[i];

            for (let j = 0; j < this.options.length; j++) {
              const option = this.options[j];

              if (model === option.value) {
                selected.push({
                  value: option.value,
                  label: option.label
                });
              }
            }
          }

          const selectedArray = [];
          const selectedObject = {};
          selected.forEach(item => {
            if (!selectedObject[item.value]) {
              selectedArray.push(item);
              selectedObject[item.value] = 1;
            }
          });

          this.selectedMultiple = this.remote ? selectedArray : selected;

          if (slot) {
            let selectedModel = [];

            for (let i = 0; i < selected.length; i++) {
              selectedModel.push(selected[i].value);
            }

            // if slot change and remove a selected option, emit user
            if (this.model.length === selectedModel.length) {
              this.slotChangeDuration = true;
            }

            this.model = selectedModel;
          }
        }
        this.toggleMultipleSelected(this.model, init);
      },
      removeTag (index) {
        if (this.disabled) {
          return false;
        }
        if (this.remote) {
          const tag = this.model[index];
          this.selectedMultiple = this.selectedMultiple.filter(item => item.value !== tag);
        }
        this.model.splice(index, 1);

        if (this.filterable && this.visible) {
          this.$refs.input.focus();
        }

        this.broadcast('oolongSelectDropdown', 'on-update-popper');
      },
      // to select option for single
      toggleSingleSelected (value, init = false) {
        if (!this.multiple) {
          let label = '';

          this.findChild(function (child) {
            if (child.value === value) {
              child.selected = true;
              label = (child.label === undefined) ? child.$el.innerHTML : child.label;
            } else {
              child.selected = false;
            }
          });

          this.hideMenu();

          if (!init) {
            if (this.labelInValue) {
              this.$emit('on-change', {
                value: value,
                label: label
              });
              this.dispatch('oolongFormItem', 'on-form-change', {
                value: value,
                label: label
              });
            } else {
              this.$emit('on-change', value);
              this.dispatch('ooolongFormItem', 'on-form-change', value);
            }
          }
        }
      },
      // to select option for multiple
      toggleMultipleSelected (value, init = false) {
        if (this.multiple) {
          let hybridValue = [];
          for (let i = 0; i < value.length; i++) {
            hybridValue.push({
              value: value[i]
            });
          }

          this.findChild(function (child) {
            const index = value.indexOf(child.value);

            if (index >= 0) {
              child.selected = true;
              hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label;
            } else {
              child.selected = false;
            }
          });

          if (!init) {
            if (this.labelInValue) {
              this.$emit('on-change', hybridValue);
              this.dispatch('oolongFormItem', 'on-form-change', hybridValue);
            } else {
              this.$emit('on-change', value);
              this.dispatch('oolongFormItem', 'on-form-change', value);
            }
          }
        }
      },
      handleClose () {
        this.hideMenu();
      },
      handleKeydown (e) {
        if (this.visible) {
          const keyCode = e.keyCode;
          // Esc slide-up
          if (keyCode === 27) {
            e.preventDefault();
            this.hideMenu();
          }
          // next
          if (keyCode === 40) {
            e.preventDefault();
            this.navigateOptions('next');
          }
          // prev
          if (keyCode === 38) {
            e.preventDefault();
            this.navigateOptions('prev');
          }
          // enter
          if (keyCode === 13) {
            e.preventDefault();

            this.findChild((child) => {
              if (child.isFocus) {
                child.select();
              }
            });
          }
        }
      },
      navigateOptions (direction) {
        if (direction === 'next') {
          const next = this.focusIndex + 1;
          this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next;
        } else if (direction === 'prev') {
          const prev = this.focusIndex - 1;
          this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev;
        }

        let child_status = {
          disabled: false,
          hidden: false
        };

        let find_deep = false;    // can next find allowed

        this.findChild((child) => {
          if (child.index === this.focusIndex) {
            child_status.disabled = child.disabled;
            child_status.hidden = child.hidden;

            if (!child.disabled && !child.hidden) {
              child.isFocus = true;
            }
          } else {
            child.isFocus = false;
          }

          if (!child.hidden && !child.disabled) {
            find_deep = true;
          }
        });

        this.resetScrollTop();

        if ((child_status.disabled || child_status.hidden) && find_deep) {
          this.navigateOptions(direction);
        }
      },
      resetScrollTop () {
        const index = this.focusIndex - 1;
        let bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
        let topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;

        if (bottomOverflowDistance > 0) {
          this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
        }
        if (topOverflowDistance < 0) {
          this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
        }
      },
      handleBlur () {
        setTimeout(() => {
          const model = this.model;

          if (this.multiple) {
            this.query = '';
          } else {
            if (model !== '') {
              this.findChild((child) => {
                if (child.value === model) {
                  this.query = child.label === undefined ? child.searchLabel : child.label;
                }
              });
              // 如果删除了搜索词，下拉列表也清空了，所以强制调用一次remoteMethod
              if (this.remote && this.query !== this.lastQuery) {
                this.$nextTick(() => {
                  this.query = this.lastQuery;
                });
              }
            } else {
              this.query = '';
            }
          }
        }, 300);
      },
      resetInputState () {
        this.inputLength = this.$refs.input.value.length * 12 + 20;
      },
      handleInputDelete () {
        if (this.multiple && this.model.length && this.query === '') {
          this.removeTag(this.model.length - 1);
        }
      },
      // use when slot changed
      slotChange () {
        this.options = [];
        this.optionInstances = [];
      },
      setQuery (query) {
        if (!this.filterable) return;
        this.query = query;
      },
      modelToQuery() {
        if (!this.multiple && this.filterable && this.model !== undefined) {
          this.findChild((child) => {
            if (this.model === child.value) {
              if (child.label) {
                this.query = child.label;
              } else if (child.searchLabel) {
                this.query = child.searchLabel;
              } else {
                this.query = child.value;
              }
            }
          });
        }
      },
      broadcastQuery (val) {
        if (findComponentDownward(this, 'OptionGroup')) {
          this.broadcast('OptionGroup', 'on-query-change', val);
          this.broadcast('iOption', 'on-query-change', val);
        } else {
          this.broadcast('iOption', 'on-query-change', val);
        }
      }
    },
    mounted () {
      this.modelToQuery();
      // 处理 remote 初始值
      if (this.remote) {
        if (!this.multiple && this.model !== '') {
          this.selectToChangeQuery = true;
          if (this.label === '') this.label = this.model;
          this.lastQuery = this.label;
          this.query = this.label;
        } else if (this.multiple && this.model.length) {
          if (this.label.length !== this.model.length) this.label = this.model;
          this.selectedMultiple = this.model.map((item, index) => {
            return {
              value: item,
              label: this.label[index]
            };
          });
        }
      }
      this.$nextTick(() => {
        this.broadcastQuery('');
      });

      this.updateOptions(true);
      document.addEventListener('keydown', this.handleKeydown);

      this.$on('append', () => {
        if (!this.remote) {
          this.modelToQuery();
          this.$nextTick(() => {
            this.broadcastQuery('');
          });
        } else {
          this.findChild((child) => {
            child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
          });
        }
        this.slotChange();
        this.updateOptions(true, true);
      });
      this.$on('remove', () => {
        if (!this.remote) {
          this.modelToQuery();
          this.$nextTick(() => {
            this.broadcastQuery('');
          });
        } else {
          this.findChild((child) => {
            child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
          });
        }
        this.slotChange();
        this.updateOptions(true, true);
      });
      this.$on('on-select-selected', function (value) {
        // alert('on-select-selected');
        if (this.model === value) {
          this.hideMenu();
        } else {
//          alert(this.multiple);
          if (this.multiple) {
            const index = this.model.indexOf(value);
            if (index >= 0) {
              this.removeTag(index);
            } else {
              //如果不是数组 设置成数组
              if (!Array.isArray(this.model)) {
                this.model = [];
              }
//              alert(Array.isArray(this.model));
              this.model.push(value);
              this.broadcast('oolongSelectDropdown', 'on-update-popper');
            }

            if (this.filterable) {
              // remote&filterable&multiple时，一次点多项，不应该设置true，因为无法置为false，下次的搜索会失效
              if (this.query !== '') this.selectToChangeQuery = true;
              this.query = '';
              this.$refs.input.focus();
            }
          } else {

            this.model = value;

            if (this.filterable) {
              this.findChild(function (child,_this) {
                if (child.value === value) {
                  if (_this.query !== '') _this.selectToChangeQuery = true;
                  _this.lastQuery = _this.query = child.label === undefined ? child.searchLabel : child.label;
                }
              },this);
            }
          }

        }
      });

    },
    beforeDestroy () {
      document.removeEventListener('keydown', this.handleKeydown);
    },
    watch: {
      value (val) {
        this.model = val;
        if (val === '') this.query = '';
      },
      model () {
        this.$emit('input', this.model);
        this.modelToQuery();
        if (this.multiple) {
          if (this.slotChangeDuration) {
            this.slotChangeDuration = false;
          } else {
            this.updateMultipleSelected();
          }
        } else {
          this.updateSingleSelected();
        }
        // #957
        if (!this.visible && this.filterable) {
          this.$nextTick(() => {
            this.broadcastQuery('');
          });
        }
      },
      visible (val) {
        if (val) {
          if (this.filterable) {
            if (this.multiple) {
              this.$refs.input.focus();
            } else {
              this.$refs.input.select();
            }
            if (this.remote) {
              this.findChild((child) => {
                child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
              });
              // remote下，设置了默认值，第一次打开时，搜索一次
              const options = this.$slots.default || [];
              if (this.query !== '' && !options.length) {
                this.remoteMethod(this.query);
              }
            }
          }
          this.broadcast('oolongSelectDropdown', 'on-update-popper');
        } else {
          if (this.filterable) {
            this.$refs.input.blur();
            // #566 reset options visible
            setTimeout(() => {
              this.broadcastQuery('');
            }, 300);
          }
          this.broadcast('oolongSelectDropdown', 'on-destroy-popper');
        }
      },
      query (val) {
          alert("query");
        if (this.remote && this.remoteMethod) {
          if (!this.selectToChangeQuery) {
            this.$emit('on-query-change', val);
            this.remoteMethod(val);
          }
          this.focusIndex = 0;
          this.findChild(function (child) {
            child.isFocus = false;
          });
        } else {
          if (!this.selectToChangeQuery) {
            this.$emit('on-query-change', val);
          }
          this.broadcastQuery(val);
          let is_hidden = true;

          this.$nextTick(function () {
            this.findChild(function (child) {
              if (!child.hidden) {
                is_hidden = false;
              }
            });
            this.notFound = is_hidden;
          });
        }
        this.selectToChangeQuery = false;
        this.broadcast('oolongSelectDropdown', 'on-update-popper');
      }
    }
  };
</script>
